package org.axed.user.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.Widget;
import org.axed.user.client.impl.InputCatcherImpl;

/**
 * The inputcatcher is a hidden textarea which actually captures all keyboard input for axed.
 * When the hidden inputcatcher is focused, Axed does as it would be focused...
 */
public class InputCatcher extends TextArea implements AxedListener, FocusListener {
	public static final int KEY_BACKSPACE   = 8;
	public static final int KEY_TAB         = 9;
	public static final int KEY_RETURN      = 13;
	public static final int KEY_ESC         = 27;
	public static final int KEY_PAGEUP      = 33;
	public static final int KEY_PAGEDOWN    = 34;
	public static final int KEY_END         = 35;
	public static final int KEY_POS1        = 36;
	public static final int KEY_LEFT_ARROW  = 37;
	public static final int KEY_UP_ARROW    = 38;
	public static final int KEY_RIGHT_ARROW = 39;
	public static final int KEY_DOWN_ARROW  = 40;
	public static final int KEY_INSERT      = 45;
	public static final int KEY_DEL         = 46;

	/**
	 * Native implementation for browserspecific keyhandling.
	 * Unfortunally every browser has a little different idea on how
	 * to send keys, repeated keys, and spezial keys like arrows.
	 */
	private static InputCatcherImpl impl;
  	static {
    	impl = (InputCatcherImpl) GWT.create(InputCatcherImpl.class);
  	}

	/**
	 * The AxedArea this belongs to.
	 */
	AxedArea axed;

	/**
	 * The caret to position to.
	 */
	Caret caret;

	/**
	 * don't react on any events if disabled
	 */
	boolean disable = false;

	/**
	 * True when ctrl+v was pressed, and the content should be pasted on keyup
	 */
	public boolean paste = false;

	/**
	 * Constructor
	 */
	public InputCatcher(AxedArea axed, Caret caret) {
		super();
		this.axed = axed;
		this.caret = caret;
		this.addFocusListener(this);
		DOM.setStyleAttribute(getElement(), "position", "absolute");
		DOM.setStyleAttribute(getElement(), "left", "10px");
		// do not position it yet to the AxedArea, because in this constructor 
		// it might not yet be appeneded to body.
		DOM.setStyleAttribute(getElement(), "top", "10px");
		DOM.setStyleAttribute(getElement(), "height", "1px");
		DOM.setStyleAttribute(getElement(), "width", "1px");
		DOM.setStyleAttribute(getElement(), "zIndex", "-10");

		sinkEvents(Event.ONKEYDOWN);
		sinkEvents(Event.ONKEYPRESS);
		sinkEvents(Event.ONKEYUP);
	}

	/**
	 * Returns true if this is a known spezial keycode
	 */
	public static final boolean isSpezial(int keycode) {
		switch (keycode) {
		case KEY_BACKSPACE :
		case KEY_TAB :
		case KEY_RETURN :
		case KEY_ESC :
		case KEY_PAGEUP :
		case KEY_PAGEDOWN :
		case KEY_END :
		case KEY_POS1 :
		case KEY_LEFT_ARROW :
		case KEY_UP_ARROW :
		case KEY_RIGHT_ARROW :
		case KEY_DOWN_ARROW :
		case KEY_INSERT :
		case KEY_DEL :
			return true;
		default :
			return false;
		}
	}

	/**
	 * Called by GWT on browser events.
	 */
	public void onBrowserEvent(Event event) {
		super.onBrowserEvent(event);
		if (disable) {
			return;
		}
		int type = DOM.eventGetType(event);
		if ((type & Event.KEYEVENTS) == 0) {
			return;
		}
		if (type == Event.ONKEYUP) {
			if (paste) {
				paste = false;
				String s = getText().replaceAll("\r", "");
				axed.paste(axed.createAtomicID(), s);
			}
			setText("");
		}
		/*{
	   		String s = "";
			switch (type) {
			case Event.ONKEYDOWN  : s = "D"; break;
			case Event.ONKEYUP    : s = "S"; break;
			case Event.ONKEYPRESS : s = "P"; break;
			}
			AxedArea.debug(true, "(" + s + DOM.eventGetKeyCode(event) + ")");
		}*/

		int key = impl.onKeyEvent(event);
		if (key == 0) {
			return;
		}

		// cancel the event since the textbox contents are hardly interesting 
		// except this is a CTRL+C or CTRL+V, where the browser should copy/paste
		// Cancalation of events helps IE6.0 to not crash soooo much :)
        // sbv hack - added metakey to support mac platform
		if (DOM.eventGetCtrlKey(event) || DOM.eventGetMetaKey(event)) {
			switch (key) {
			case 'c' :
			case 'C' :
			case 'v' :
			case 'V' :
				break;
			default:
				DOM.eventPreventDefault(event);
			}
		} else {
			DOM.eventPreventDefault(event);
		}

		// Prevent refresh on CTRL+R
		//if (DOM.eventGetCtrlKey(event) && (key == 'r' || key == 'R')) {
		//	DOM.eventPreventDefault(event);
		//}


		char ch = 0;
		if (key > 0) {
			ch = (char) key;
		}

		if (AxedArea.DEBUG_KEYBOARD_EVENTS) {
			String s = "";
			switch (type) {
			case Event.ONKEYDOWN  : s = "D"; break;
			case Event.ONKEYUP    : s = "S"; break;
			case Event.ONKEYPRESS : s = "P"; break;
			}
			s += key + " ";
			axed.debug(true, s);
		}
        // sbv hack - added metakey to support mac platform
		axed.keypress(key, ch, DOM.eventGetShiftKey(event), DOM.eventGetCtrlKey(event) || DOM.eventGetMetaKey(event));
	}

    /**
	 * Fired when a widget receives keyboard focus.
	 */
	public void onFocus(Widget sender) {
		if (disable) {
			return;
		}
		axed.onFocus(sender);
	}
	
	/**
	 * Fired when a widget loses keyboard focus.
	 */
	public void onLostFocus(Widget sender) {
		if (disable) {
			return;
		}
		axed.onLostFocus(sender);
    }

	/** 
	 * Handles events generated by AxedArea. 
	 * Positions the caret when necessary.
	 */
	public void onAxedEvent(AxedEvent event) {
		switch (event.type) {
		case AxedEvent.FONT_SIZE_CHANGED :
		case AxedEvent.MOVE_CURSOR : 
		case AxedEvent.REFRESH : 
		case AxedEvent.SCROLL :
			{
			int left = caret.left;
			int top = caret.top;
			if (left < 5) {
				left = 5;
			}
			if (top < 5) {
				top = 5;
			}
			if (left > axed.getClientWidth() - 5) {
				left =  axed.getClientWidth() - 5;
			}
			if (top > axed.getClientHeight() - 5) {
				top =  axed.getClientHeight() - 5;
			}

			DOM.setStyleAttribute(getElement(), "left", (left + axed.getAbsoluteLeft()) + "px");
			DOM.setStyleAttribute(getElement(), "top",  (top  + axed.getAbsoluteTop())  + "px");
			break;
			}
		}
	}

}

